package org.loong.crypto.service.software.provider.asymmetric.rsa;

import java.security.interfaces.RSAPrivateKey;
import java.util.Objects;
import java.util.Optional;

import org.loong.crypto.common.exception.CryptoException;
import org.loong.crypto.common.exception.InvalidKeyException;
import org.loong.crypto.common.exception.InvalidKeyLengthException;
import org.loong.crypto.core.algorithm.CipherAlgorithm;
import org.loong.crypto.core.params.CipherParameters;
import org.loong.crypto.core.utils.RSAKeyUtils;
import org.loong.crypto.service.core.provider.Decrypter;
import org.loong.crypto.service.core.provider.impl.RSACipherProvider;

import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;

public class RSADecrypter extends RSACipherProvider implements Decrypter {

    /**
     * The private key.
     */
    private RSAPrivateKey privateKey;

    /**
     * The rsa decrypt algorithm.
     */
    private String rsaAlgorithm;

    /**
     * The rsa key length.
     */
    private Integer keyLength;
    
    /**
     * Creates a new decrypter.
     *
     * @param keyBytes the private key bytes. Must not be {@code null}.
     */
    public RSADecrypter(final byte[] keyBytes) {
        this(RSAKeyUtils.toRSAPrivateKey(keyBytes));
    }

    /**
     * Creates a new decrypter.
     *
     * @param privateKey the private key. Must not be {@code null}.
     */
    public RSADecrypter(final RSAPrivateKey privateKey) {
        if (privateKey == null) {
            throw new InvalidKeyException("The private RSA key must not be null.");
        }

        if (!"RSA".equalsIgnoreCase(privateKey.getAlgorithm())) {
            throw new InvalidKeyException("The private key algorithm must be RSA.");
        }

        this.privateKey = privateKey;
    }

    @Override
    public void init(CipherParameters parameters) {
        if (Objects.nonNull(parameters.getEncryptionMethod())) {
            this.rsaAlgorithm = parameters.getEncryptionMethod().getAlgorithm();
            this.keyLength = parameters.getEncryptionMethod().getKeyLength();
        }
    }

    @Override
    public byte[] decrypt(CipherAlgorithm algorithm, byte[] cipherText) throws CryptoException {
        // key size in bits
        int bitLength = privateKey.getModulus().bitLength();
        if (Objects.nonNull(keyLength) && bitLength < keyLength) {
            throw new InvalidKeyLengthException("The key length must be at least " + keyLength + " bits.");
        }
        
        try {
            RSA rsa = new RSA(Optional.ofNullable(rsaAlgorithm).orElse(algorithm.getName()), privateKey, null);
            return rsa.decrypt(cipherText, KeyType.PrivateKey);
        } catch (cn.hutool.crypto.CryptoException e) {
            throw new CryptoException("RSA decrypt exception: " + e.getMessage(), e);
        }
    }
}
